<?php
require_once 'GridLayouter.php';

class FeedsController extends Zend_Controller_Action
{

    public function init()
    {
        /* Initialize action controller here */
    }

    /**
     * Updates the feeds, add new entries, etc.
     * @param int $id The id of the user
     * @param mixed $account The account of the user
     * @param mixed $accounts The Accounts database
     * @param mixed $feeds The Feeds database
     * @param mixed $feedEntries the FeedEntries database
     * @param mixed $accountFeeds The AccountFeeds database
     * @param mixed $tokens The Tokens database
     * @param mixed $accountTokens The AccountTokens database
     */
    private function updateFeeds($id, $account, $accounts, $feeds, $feedEntries, $accountFeeds, $tokens, $accountTokens)
    {
        $feedRows = $accounts->getFeedsById($id);
        $token_count = 0;
        $yesterday = Zend_Date::now();
        $yesterday->setHour("6");
        $yesterday->setMinute("0");
        $yesterday->setSecond("0");
        $yesterday->setMilliSecond("0");
        $yesterday->subDay(1);
        $token_count = 0;
        foreach ($feedRows as $feedRow)
        {
            $feed = Application_Model_Feed::fromTableRow($feedRow);
            $entries = Application_Model_FeedEntry::entriesFromFeed($feed->url, $yesterday);
            if (!$entries)
                continue;
            foreach ($entries as $entry)
            {
                $entryRow = $feedEntries->getByRssEntryId($feed->id, $entry->rss_entry_id);
                if (!$entryRow)
                {
                    $entry->id = $feedEntries->insertFeedEntry($entry->rss_entry_id,
                        $feed->id,
                        $entry->title,
                        $entry->link,
                        $entry->description,
                        $entry->author,
                        $entry->publish_time,
                        $entry->enclosure);
                }
                else
                {
                    $entry->id = $entryRow['feed_entry_id'];
                }
                $tokenized = self::tokenise($entry->title);
                foreach ($tokenized as $token)
                {
                    $accountTokenRow = $accountTokens->getByToken($account->id, $token);
                    if (!$accountTokenRow)
                    {
                        $token_id = 0;
                        $tokenRow = $tokens->getByValue($token);
                        if (!$tokenRow)
                        {
                            $token_id = $tokens->insertToken($token);
                        }
                        else
                        {
                            $token_id = $tokenRow['token_id'];
                        }
                        $accountTokens->insertAccountToken($account->id, $token_id);
                        $token_count += 1;
                    }
                }
            }
        }
        $account->classCount['pos'] += (int)$token_count;
        $account->tokenCount += (int)$token_count;
        $accounts->updateTokenCounts($account->id,
            (int)$account->classCount['pos'],
            (int)$account->classCount['neg'],
            (int)$account->tokenCount);
    }

    /**
     * Updates the learning algorithm from yesterday's gridboxes. Does nothing if nothing was read.
     * @param int $accountId The id of the user's account
     * @param Application_Model_Account $account The account
     * @param Application_Model_DbTable_Accounts $accounts The Accounts database
     * @param Application_Model_DbTable_FeedEntries $feedEntries The FeedEntries database
     * @param Application_Model_DbTable_GridBoxes $gridBoxes The GridBoxes database
     * @param Application_Model_DbTable_AccountTokens $accountTokens The AccountTokens database
     */
    private function updateGridBoxes($accountId,
                                    Application_Model_Account $account,
                                    Application_Model_DbTable_Accounts $accounts,
                                    Application_Model_DbTable_FeedEntries $feedEntries,
                                    Application_Model_DbTable_GridBoxes $gridBoxes,
                                    Application_Model_DbTable_AccountTokens $accountTokens)
    {
        $readCount = $gridBoxes->getNumberOfReadById($accountId);
        if ($readCount == 0)
            return;
        $gridRows = $gridBoxes->getByAccountId($accountId);
        $augment = array(true => array('pos_count' => 1, 'neg_count' => 0),
                        false => array('neg_count' => 0, 'pos_count' => 0));
        foreach ($gridRows as $gridRow)
        {
            $tokens = $this->tokenise($gridRow->feed_entry->title);
            foreach ($tokens as $token)
            {
                $aug = $augment[$gridRow->isRead];
                $tokenRow = $accountTokens->getByToken($accountId, $token);
                if ($tokenRow)
                {
                    $accountTokens->updateCounts($accountId,
                        $tokenRow['token_id'],
                        $tokenRow['pos_count'] + $aug['pos_count'],
                        $tokenRow['neg_count'] + $aug['neg_count']);
                    $account->classCount['pos'] += (int)$aug['pos_count'];
                    $account->classCount['neg'] += (int)$aug['neg_count'];
                }
                else
                {
                }
            }
        }
        $account->tokenCount = $account->classCount['pos'] + $account->classCount['neg'];
        $accounts->updateTokenCounts($accountId,
            $account->classCount['pos'],
            $account->classCount['neg'],
            $account->tokenCount);
    }

    /**
     * Performs all the calculation and layouting.
     */
    public function indexAction()
    {
        $session = new Zend_Session_Namespace("Auth");
        $accountId = (int)$session->id;
        $gridBoxes = new Application_Model_DbTable_GridBoxes();
        $accounts = new Application_Model_DbTable_Accounts();
        $account = Application_Model_Account::fromTableRow($accounts->getById($accountId));
		
		$todayUpdateTime = Zend_Date::now();
		if ($todayUpdateTime->get(Zend_Date::HOUR) < 6)
			$todayUpdateTime->subDay(1);
        $todayUpdateTime->setHour("6");
        $todayUpdateTime->setMinute("0");
        $todayUpdateTime->setSecond("0");
        $todayUpdateTime->setMilliSecond("0");
        if ($account->lastUpdateTime->isEarlier($todayUpdateTime) || isset($_GET['forceUpdate']))
        {
            $feedEntries = new Application_Model_DbTable_FeedEntries();
            $feeds = new Application_Model_DbTable_Feeds();
            $accountTokens = new Application_Model_DbTable_AccountTokens();
            $accountFeeds = new Application_Model_DbTable_AccountFeeds();
            $tokensDb = new Application_Model_DbTable_Tokens();
            $this->updateGridBoxes($accountId, $account, $accounts, $feedEntries, $gridBoxes, $accountTokens);
            $this->updateFeeds($accountId,
                $account,
                $accounts,
                $feeds,
                $feedEntries,
                $accountFeeds,
                $tokensDb,
                $accountTokens);
            // Then, we load all the feed entries for the user that are more recent
            // than the last update time
            $entries = $feedEntries->getByAccountId($accountId,
                $account->lastUpdateTime->get(Zend_Date::TIMESTAMP));
            /*Compute weights for each one
            It's a Naive Bayes filter
            Look at: http://phpir.com/bayesian-opinion-mining
            for more information on the algorith.*/
            $feedWeights = array();
            foreach ($entries as $entry)
            {
                // FIXME: we may use article short description in place of the title to
                // determine if it is of interest to the reader.
                $tokens = $this->tokenise($entry['title']);
                $classes = array('pos', 'neg');
                $prior = array('pos' => 0.5, 'neg' => 0.5);
                $classScores = array();
                foreach ($classes as $class)
                    $classScores[$class] = 1;
                foreach ($tokens as $token)
                {
                    $tokenStats = $accountTokens->getByToken($accountId, $token);
                    foreach ($classes as $class)
                    {
                        $count = (int)(($tokenStats != null) ? $tokenStats[$class . '_count'] : 0);
                        $classScores[$class] *= ($count + 1) / max(0,
                            ($account->classCount[$class] + $account->tokenCount));
                    }
                }
                $totalWeight = 0;
                foreach ($classes as $class)
                {
                    $classScores[$class] = $prior[$class] * $classScores[$class];
                    $totalWeight += $classScores[$class];
                }
                foreach ($classes as $class)
                    $classScores[$class] /= max(0, $totalWeight);
                array_push($feedWeights, new GridEntry($classScores['pos'], $entry));
            }
            usort($feedWeights, 'GridLayouter::entryCompareReverse');
            $subFeedWeights = array_slice($feedWeights, 0, $account->max_newnits);
            $layouter = new GridLayouter();
            $gridSize = new Size(10, 10);
            $newnits = $layouter->layout($subFeedWeights, $gridSize);
            $gridBoxes->deleteByAccountId($accountId);
            foreach ($newnits as $newnit)
            {
                $ent = $newnit->gridEntry->entry;
                if ($ent == null)
                    continue;
                $bounds = $newnit->bounds;
                $gridBoxes->insertGridBox($accountId,
                    $ent['feed_entry_id'],
                    $bounds->location->x,
                    $bounds->location->y,
                    $bounds->size->width,
                    $bounds->size->height);
            }
            $accounts->updateLastUpdateTime($accountId, Zend_Date::now()->get(Zend_Date::TIMESTAMP));
        }
        $rows = $gridBoxes->getByAccountId($accountId);
        $this->view->gridBoxes = $rows;
        $this->_helper->layout->setLayout('grid');
    }

    /**
     * Redirects the user to an external page, updating the values in the database
     */
    public function redirectAction()
    {
        if ($this->_hasParam('feid'))
        {
            $feid = $this->_getParam('feid');
            $session = new Zend_Session_Namespace('Auth');
            $accountId = $session->id;
            $feedEntryId = $feid;
            $gridBoxes = new Application_Model_DbTable_GridBoxes();
            $gridBoxes->setIsRead($accountId, $feedEntryId, true);
            $gridBox = $gridBoxes->getById($accountId, $feedEntryId);
            $this->_redirect($gridBox->feed_entry->link);
        }
    }

    private static function tokenise($document)
    {
        $document = strtolower($document);
        preg_match_all('/\w+/', $document, $matches);
        return $matches[0];
    }
}

